package com.basker.pisces.resource;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.LocaleUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.NamedThreadLocal;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import com.basker.pisces.exception.ExceptionFactory;

/**
 * 字符串资源类，通过该类获取系统在配置文件中定义的各类静态文本资源。
 *
 * <p>
 * 支持国际化——资源文件以标准的语言与国家代码结尾，例如：xx_resouce_xx.ec_US.properties。
 * <p>
 * 资源值支持占位符号，例如：你好：{0}！，占位符的值通过api传入。
 *
 * @author hangwen
 * @see #get(String, Object...)
 * @see #get(String, Locale, Object...)
 */
public class StringResources {
    private static final String RESOURCE_KEY_REGEX = "\\$\\{.*?\\}";
    private static final Pattern RESOURCE_KEY_PATTERN = Pattern.compile(RESOURCE_KEY_REGEX);

    private static Map<Locale/* 国际化对象 */, Map<String, String>/* 对应该国际化的文本资源 */> ALL_LOCALE_RESOURCES = new HashMap<>(4);
    /**
     * 默认语言的文本资源
     */
    private static Map<String, String> DEFAULT_LOCALE_RESOURCES = null;

    private static final Object[] EMPTY_ARGUMENTS = {};

    private static final ThreadLocal<Boolean> RESOLVE_KEY_DISABLE = new NamedThreadLocal<>("RESOLVE_KEY_DISABLE");

    public static void clear() {
        ALL_LOCALE_RESOURCES.clear();
        DEFAULT_LOCALE_RESOURCES = null;
    }

    /**
     * 从指定语言中获取指定的资源值，如果指定语言不存在，则返回默认语言对应的值
     *
     * @param key
     * @param locale    语言
     * @param arguments 如果值里包含占位符，通过此传入参数。
     * @return
     */
    public static String get(String key, Locale locale, Object... arguments) {
        if (locale == null) {
            locale = LocaleContextHolder.getLocale();
        }

        Map<String, String> resources = ALL_LOCALE_RESOURCES.get(locale);
        if (resources == null) {
            resources = DEFAULT_LOCALE_RESOURCES;
        }

        if (resources == null) {
            return null;
        }

        String res = resources.get(key);

        // 如果没有从非默认语言的资源中找到对应的项，那么从默认语言再找一次
        if (res == null && resources != DEFAULT_LOCALE_RESOURCES) {
            return get(key, Locale.getDefault(), arguments);
        }

        if (!StringUtils.isEmpty(res) && !ArrayUtils.isEmpty(arguments)) {
            res = MessageFormat.format(res, arguments);
        }

        return res;

    }

    /**
     * 从默认语言中获取指定的资源值
     *
     * @param key
     * @param arguments 如果值里包含占位符，通过此传入参数。
     * @return
     */
    public static String get(String key, Object... arguments) {
        return get(key, LocaleContextHolder.getLocale(), arguments);
    }

    /**
     * 读取资源，加载所有语言的文本资源
     *
     * @param resourceList
     * @throws IOException
     */
    public synchronized static void load(Resource[] resourceList) throws IOException {
        for (Resource resource : resourceList) {
            load(resource);
        }
    }

    /**
     * 如果resolvable中包括${key}的格式，则对其进行翻译,如果没有对应资源则返回null
     *
     * @param resolvable
     * @param locale
     * @param arguments
     * @return
     */
    public static String resolve(String resolvable, Locale locale, Object... arguments) {
        if (Boolean.TRUE.equals(RESOLVE_KEY_DISABLE.get())) {
            return resolvable;
        }

        Matcher matcher = RESOURCE_KEY_PATTERN.matcher(resolvable);
        List<String> keys = null;

        while (matcher.find()) {
            String group = matcher.group();
            String key = group.substring(2, group.length() - 1);

            if (keys == null) {
                keys = new ArrayList<>();
            }

            keys.add(key);
        }

        if (keys != null) {
            String resolved = resolvable;

            // 有多个key匹配，就不支持传入参数了
            boolean userArgs = keys.size() == 1;

            for (String key : keys) {
                if (!userArgs) {
                    arguments = EMPTY_ARGUMENTS;
                }

                String message = StringResources.get(key, locale, arguments);
                if (message != null) {
                    resolved = resolved.replaceFirst(RESOURCE_KEY_REGEX, message);
                }
            }

            return resolved;
        }

        return resolvable;
    }

    /**
     * 如果resolvable中包括${key}的格式，则对其进行翻译
     *
     * @param resolvable
     * @param arguments
     * @return
     */
    public static String resolve(String resolvable, Object... arguments) {
        return resolve(resolvable, null, arguments);
    }

    /**
     * 当前线程恢复{@link #resolve(String, Locale, Object...)}方法的功能
     */
    public static void resumeResovle() {
        RESOLVE_KEY_DISABLE.remove();
    }

    /**
     * 当前线程禁止{@link #resolve(String, Locale, Object...)}方法的功能，直接返回参数
     */
    public static void suspandResovle() {
        RESOLVE_KEY_DISABLE.set(Boolean.TRUE);
    }

    private static Locale getResourceLocale(String filename) {
        String[] split = filename.replaceAll("\\.properties", "").split("\\.");
        // filename.locale
        int fileNameWithLocaleSplitCount = 2;

        if (split.length == fileNameWithLocaleSplitCount) {
            String localStr = split[1];
            try {
                return LocaleUtils.toLocale(localStr);
            } catch (IllegalArgumentException e) {
                throw ExceptionFactory.createRuntimeException("pisces-common.StringResources.getResourceLocale-001",
                        filename);
            }
        }

        return Locale.getDefault();
    }

    private static void load(Resource resource) throws IOException {
        Locale locale = getResourceLocale(resource.getFilename());

        Map<String, String> resources = ALL_LOCALE_RESOURCES.get(locale);
        if (resources == null) {
            resources = new HashMap<String, String>(64);
            ALL_LOCALE_RESOURCES.put(locale, resources);

            if (locale.equals(Locale.getDefault())) {
                DEFAULT_LOCALE_RESOURCES = resources;
            }
        }

        // 处理中文乱码问题，系统的文件默认为UTF-8编码，所以这里要明确编码，否则java默认会用的ISO-8859-1
        Properties properties = PropertiesLoaderUtils.loadProperties(new EncodedResource(resource, "UTF-8"));

        for (Entry<Object, Object> entry : properties.entrySet()) {
            resources.put((String) entry.getKey(), (String) entry.getValue());
        }
    }
}
